package com.nowcoder.topic.dp.hard;

import java.util.ArrayList;

/**
 * NC393 取金币
 * @author d3y1
 */
public class NC393{
    /**
     * 代码中的类名、方法名、参数名已经指定，请勿修改，直接返回方法规定的值即可
     *
     *
     * @param coins int整型一维数组
     * @return int整型
     */
    public int getCoins (ArrayList<Integer> coins) {
        return solution(coins);
    }

    /**
     * 动态规划
     *
     * 相似 -> NC326 能量项链
     *
     * dp[i][j]表示选择区间[i,j]金币能得到的最多积分
     *
     *            { Math.max(dp[i][j], dp[k+1][j]+coins[i-1]*coins[k]*coins[j+1])             , k=i   && 1<=i<=j<=n
     * dp[i][j] = { Math.max(dp[i][j], dp[i][k-1]+coins[i-1]*coins[k]*coins[j+1])             , k=j   && 1<=i<=j<=n
     *            { Math.max(dp[i][j], dp[i][k-1]+dp[k+1][j]+coins[i-1]*coins[k]*coins[j+1])  , i<k<j && 1<=i<=j<=n
     *
     * @param coinList
     * @return
     */
    private int solution(ArrayList<Integer> coinList){
        int n = coinList.size();
        int[] coins = new int[n+2];
        for(int i=0; i<n; i++){
            coins[i+1] = coinList.get(i);
        }
        coins[0] = 1;
        coins[n+1] = 1;

        int[][] dp = new int[n+2][n+2];

        // 滑动窗口
        for(int gap=0; gap<n; gap++){
            // 区间[i,j]能得到的最多积分
            for(int i=1,j=i+gap; j<=n; i++,j++){
                // [i,k-1] k [k+1,j] <- k:区间[i,j]最后选择金币位置索引
                for(int k=i; k<=j; k++){
                    // 最后选左边界
                    if(k == i){
                        dp[i][j] = Math.max(dp[i][j], dp[k+1][j]+coins[i-1]*coins[k]*coins[j+1]);
                    }
                    // 最后选右边界
                    else if(k == j){
                        dp[i][j] = Math.max(dp[i][j], dp[i][k-1]+coins[i-1]*coins[k]*coins[j+1]);
                    }
                    // 最后选区间中间
                    else{
                        dp[i][j] = Math.max(dp[i][j], dp[i][k-1]+dp[k+1][j]+coins[i-1]*coins[k]*coins[j+1]);
                    }
                }
            }
        }

        return dp[1][n];
    }
}